/*
Waterfall Hash Algorithm version 1.7 Optimised 32-bit version A

Generates a fixed length message digest of up to 512 bits in 32-bit increments from an 
unlimited-length message, with resistance to a range of cryptographic attacks.

See "Waterfall Specification 1.0" for details.

Bob Hattersley 12Oct08
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>		// For NULL
#include "waterfall.h"

#define FINALUPDATES 16	// Number of Update Steps in Final with fixed input
#define FINALSTEPS 4	// Number of Final Steps in Final after combining with Pools

#define LITTLEENDIAN 1	// If we can assume little-endian byte order - yes for Intel
#define UNROLL 1		// If we do best with loops unrolled - yes
#define DEBUG 0			// 0: no output, 1: API calls, 2: input, main step results, 3: full processing breakdown
#if DEBUG>0
#include <stdio.h>
#endif

#define ALGORITHM_NAME "waterfall"
#define	MAX_MARKER_LEN		50
#define	SUBMITTER_INFO_LEN	128
// -------------------------------------------------------------------------------------------------

// API routine to initialise hash algorithm state and check required message digest length

HashReturn Init ( hashState *state, int hashbitlen ) {
	int i;

// Check message digest length is valid (whole 32-bit words, at most 16 of them)
	if (hashbitlen<=0 || hashbitlen>32*STREAM1 || hashbitlen%32) return BAD_HASHBITLEN;
	state->hashbitlen = hashbitlen;

// Initialise input buffer to empty
	state->inputbits = 0;
	state->inputblocks = 0;
	state->inputslabs = 0;

// Put the digest length into the first word of Stream 2 (Stream 1 may be used for a key)
	state->stream2[0] = hashbitlen;

// Initialise remaining Stream entries to zeros
	for (i=0; i<STREAM1; i++) state->stream1[i] = 0;
	state->pstream1 = state->stream1 + STREAM1 - 1;
	for (i=1; i<STREAM2; i++) state->stream2[i] = 0;
	state->pstream2 = state->stream2 +  STREAM2 - 1;
	for (i=0; i<STREAM3; i++) state->stream3[i] = 0;
	state->pstream3 = state->stream3 +  STREAM3 - 1;

// Initialise Pools to zeros
	for (i=0; i<POOL; i++) state->pool2[i] = 0;
	state->ppool2 = state->pool2 + POOL-1;
	for (i=0; i<POOL; i++) state->pool3[i] = 0;
	state->ppool3 = state->pool3 + POOL-1;

#if DEBUG>0
	printf("Waterfall initialised: hashbitlen %d\n", hashbitlen);
#endif
	return SUCCESS;
}

// -------------------------------------------------------------------------------------------------

// Perform the X-box substitution on a 32-bit word

void Xbox ( unsigned int *pword ) {

// X-box substitution data, based on Rijndael S-box and 3 affine transforms
	static const unsigned int xbox[] = {
		0xbb9a4063, 0x2b668d7d, 0xa50c3275, 0xaf811c78, 0xe638eff6, 0xb793746e, 0xb1176e69, 0x4be946c2,
		0x43a30338, 0xebb4bd08, 0xbd1e5a6d, 0xd5dbd420, 0xecb5c1f2, 0x50b923da, 0x114a95a5, 0x242db479,
		0xc307e3da, 0xad467793, 0x416468db, 0xaa470b6e, 0xea31dbee, 0x9de7414c, 0x8c3a0a51, 0xe57ae2e7,
		0x148c82b5, 0xd2daa8cd, 0x9c6227b8, 0x17ce8fb4, 0xbc9b3c80, 0x99a430b9, 0x22a9ae6c, 0xcc4cdadf,
		0x03d5d397, 0x6ed64adc, 0x347599b1, 0x5e777c05, 0x46651412, 0xcb4da61a, 0x619d73d1, 0xc6c1f4eb,
		0x4527191c, 0x1885b68c, 0x7acd16cf, 0x645b64da, 0xa0ca255d, 0xd85786f5, 0xc282851f, 0xf522cf3a,
		0x6c112134, 0x48ab4bf6, 0xd9d2e011, 0x4e2f51f0, 0x7e8e672c, 0xb3d005a3, 0xed30a733, 0xb95d2bad,
		0xee72aa3f, 0x71c55e2b, 0xae047aba, 0xfe2a87d9, 0x730235d7, 0xdf56fa1a, 0x84704f8c, 0xa64e3f4a,
		0xe7bd8949, 0x2c67f1c2, 0x513c456e, 0x7dcc6a59, 0xfcedec5f, 0x3036e82b, 0x1f84ca1c, 0x9f202ae7,
		0x138dfe1a, 0xcdc9bc72, 0xd198a59c, 0x0551c9f8, 0xd699d965, 0x7f0b01ae, 0xd35fce61, 0xa88060cb,
		0x92ac7803, 0x557f3480, 0x6a953b52, 0x76c422be, 0x5bb16b74, 0xeff7cca9, 0x0613c4e7, 0x9ea54c0c,
		0x36b2f232, 0x42266592, 0x8efd61e4, 0xce8bb162, 0x0796a216, 0x0250b511, 0x1cc6c706, 0x44a27f90,
		0xd45eb2b0, 0x75862f8e, 0x906b13c8, 0x6b105d98, 0x8abe1027, 0x83713328, 0xc1c08855, 0x29a1e6e2,
		0x8f78072d, 0x68525090, 0x69d73668, 0xa9050614, 0x10cff33c, 0x492e2d51, 0x3ef8b7f1, 0x93291ec7,
		0x91ee7521, 0x1d43a1d2, 0x08dd9b32, 0x26eadffc, 0xb5541fe6, 0x3dbabae8, 0x4faa374e, 0x62df7e82,
		0x8dbf6cc4, 0x82f455cf, 0xdb158ba0, 0xda90ed5a, 0x7287536c, 0x6d944782, 0x6719698d, 0xd71cbfad,
		0x47e0724d, 0x6018158d, 0xf0e4d891, 0xf7e5a46f, 0x982156db, 0x32f18312, 0x0e5981c2, 0xf660c290,
		0xcac8c04c, 0x1bc7bb2e, 0x282480f4, 0xc80fabb6, 0x3f7dd1e8, 0x9b635bd0, 0xffafe197, 0xa38828fc,
		0x39f9cbf0, 0x2f25fc10, 0x80333edd, 0xded39c4f, 0x58f366b6, 0x54fa52bf, 0xb6161206, 0xa20d4e1f,
		0x0d1b8cde, 0xf4a7a977, 0x8b3b7622, 0x7403498f, 0xdd919142, 0x1900d0c3, 0xe4ff8495, 0x5a340d44,
		0xfd688a40, 0x40e10e93, 0x4ce83a98, 0x65de02a9, 0x85f529ed, 0x6f532ca3, 0x5d357182, 0x1a42ddfb,
		0xcf0ed76a, 0x563d397a, 0x95ad0406, 0x3abbc6c9, 0x3737943d, 0x31b38e38, 0xfbec904a, 0xacc311d6,
		0x798f1b57, 0xc045ee79, 0xc7449285, 0xb25563de, 0x25a8d239, 0x53fb2e60, 0x0112b8f8, 0x1208981e,
		0x3374e5d4, 0x1509e4ef, 0xe3fef84e, 0xf223b351, 0xbe5c57d9, 0x2ea09ac7, 0x96ef0910, 0x669c0fb7,
		0x88797b7a, 0x2de297b9, 0xdc14f7e7, 0x527e48ed, 0x780a7dd8, 0x9ae63d63, 0x81b65872, 0xc98acd01,
		0xf161be20, 0x5ff21a14, 0x276fb9be, 0xfa69f6d4, 0x86b72487, 0x0c9eea70, 0x206ec545, 0xa14f4345,
		0x21eba3a0, 0x4a6c20ef, 0x0097de67, 0x3c3fdcb5, 0x04d4af9c, 0xe8f6b0d6, 0xe0bcf520, 0x635a18d9,
		0xb8d84db9, 0xc4069fec, 0x9428628d, 0x0a1af062, 0xabc26d5a, 0x4d6d5c1c, 0xf92bfbc3, 0xbfd93141,
		0x7c490c01, 0xe973d619, 0xba1f267a, 0xf3a6d5f2, 0xb4d1798d, 0x5976003c, 0xa7cb5968, 0xb0920873,
		0x387cad73, 0x7b4870f7, 0x2ae3eb6d, 0x70403802, 0xc583f922, 0x976a6fb8, 0x57b85fc6, 0x5cb01730,
		0xa489547c, 0x1e01ac50, 0x232cc87b, 0xe13993fe, 0x0fdce74b, 0xf8ae9d13, 0x0b9f96b4, 0x35f0ff9f,
		0x89fc1db9, 0x3b3ea060, 0xd01dc3d7, 0xe27b9ef4, 0x8732424c, 0x164be9a9, 0x0958fd45, 0x774144e9
	};

#if DEBUG>2
	printf("  X-box steps: %8.8x ", *pword);
#endif
	*pword ^= xbox[*pword & 0xFF];
	*pword = (*pword<<24) | (*pword>>8);
#if DEBUG>2
	printf("%8.8x ", *pword);
#endif
	*pword ^= xbox[*pword & 0xFF];
	*pword = (*pword<<24) | (*pword>>8);
#if DEBUG>2
	printf("%8.8x ", *pword);
#endif
	*pword ^= xbox[*pword & 0xFF];
	*pword = (*pword<<24) | (*pword>>8);
#if DEBUG>2
	printf("%8.8x ", *pword);
#endif
	*pword ^= xbox[*pword & 0xFF];
	*pword = (*pword<<24) | (*pword>>8);
#if DEBUG>2
	printf("%8.8x\n", *pword);
#endif
}

// -------------------------------------------------------------------------------------------------

// Perform a single update step given an input word

// Debug statements to support inline version
#if DEBUG>2
#define UPDATEDEBUG1 printf(" Update step input %8.8x\n", input);
#define UPDATEDEBUG2																								\
	printf("  Current stream positions %d %d %d\n",																	\
		state->pstream1 - state->stream1, state->pstream2 - state->stream2, state->pstream3 - state->stream3);		\
	printf("  Previous stream values %8.8x %8.8x %8.8x\n", word1, word2, word3);									\
	printf("  Current stream values  %8.8x %8.8x %8.8x\n", *state->pstream1, *state->pstream2, *state->pstream3);
#define UPDATEDEBUG3 printf("  New stream values      %8.8x %8.8x %8.8x\n", word1, word2, word3);
#define UPDATEDEBUG4 printf("  New pool values at %d: %8.8x %8.8x\n", state->ppool2 - state->pool2, *state->ppool2, *state->ppool3);
#elif DEBUG>1
#define UPDATEDEBUG1 printf(" Update step input %8.8x\n", input);
#define UPDATEDEBUG2
#define UPDATEDEBUG3
#define UPDATEDEBUG4
#else // no debug
#define UPDATEDEBUG1
#define UPDATEDEBUG2
#define UPDATEDEBUG3
#define UPDATEDEBUG4
#endif

// Extract current stream values if not already there - depends on unrolling
#define STREAMWORDS																		\
	word1 = *state->pstream1;															\
	word2 = *state->pstream2;															\
	word3 = *state->pstream3;

// Macro version of UpdateStep for inlining
#define UPDATESTEP																		\
	UPDATEDEBUG1																		\
	if (++state->pstream1 == state->stream1+STREAM1) state->pstream1 = state->stream1;	\
	if (++state->pstream2 == state->stream2+STREAM2) state->pstream2 = state->stream2;	\
	if (++state->pstream3 == state->stream3+STREAM3) state->pstream3 = state->stream3;	\
	UPDATEDEBUG2																		\
	word1 ^= *state->pstream1 ^ input; 													\
	Xbox(&word1);																		\
	*state->pstream1 = word1;															\
	word2 ^= *state->pstream2 ^ input ^ 1; 												\
	Xbox(&word2);																		\
	*state->pstream2 = word2;															\
	word3 ^= *state->pstream3 ^ input ^ 2; 												\
	Xbox(&word3);																		\
	*state->pstream3 = word3;															\
	UPDATEDEBUG3																		\
	state->ppool2++;																	\
	if(state->ppool2==state->pool2+POOL) {												\
		state->ppool2 = state->pool2;													\
		state->ppool3 = state->pool3;													\
	} else {																			\
		state->ppool3++;																\
	}																					\
	*state->ppool2 ^= word2;															\
	*state->ppool3 ^= word3;															\
	UPDATEDEBUG4

// For Final
static void UpdateStep ( hashState *state, unsigned int input ) {
	unsigned int word1, word2, word3;
	STREAMWORDS
	UPDATESTEP
}

// Get one word out of the buffer or input data, taking care of big-endian data
#if LITTLEENDIAN
#define NEXTINPUT input = *pinput++;
#else
#define NEXTINPUT													\
	input = *pinput++;												\
	input = (input>>16) | (input<<16);								\
	input = ((input & 0xFF00FF00)>>8) | ((input & 0x00FF00FF)<<8);
#endif

// Process one block of input data (16 words) plus the block count, depending on loop unrolling
#if UNROLL
#define UPDATEBLOCK						\
	STREAMWORDS							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	NEXTINPUT							\
	UPDATESTEP							\
	input = ++state->inputblocks;		\
	if (!input) state->inputslabs++;	\
	UPDATESTEP
#else // not UNROLL
#define UPDATEBLOCK						\
	for (i=0; i<16; i++) {				\
		STREAMWORDS						\
		NEXTINPUT						\
		UPDATESTEP						\
	}									\
	STREAMWORDS							\
	input = ++state->inputblocks;		\
	if (!input) state->inputslabs++;	\
	UPDATESTEP
#endif // UNROLL

// -------------------------------------------------------------------------------------------------

// API routine to process some passed data into the hash state

HashReturn Update ( hashState *state, const BitSequence *data, DataLength databitlen ) {
	unsigned long long left;
	int ibuff, ibits, i;
	unsigned int input, *pinput;
	unsigned int word1, word2, word3;
	unsigned char last;

#if DEBUG>0
	printf("Update: databitlen %d\n", databitlen);
#endif

// Number of bytes left in the input data
	left = databitlen / 8;

// If there is something in the buffer, fill up the buffer first
	ibuff = state->inputbits / 8;

	if (ibuff) {
		while (ibuff<64 && left>0) {
			state->inputbuffer[ibuff++] = *data++;
			left--;
		}

// If the buffer is now full, process the buffer, then mark it empty
		if (ibuff==64) {
			pinput = (unsigned int *) state->inputbuffer;
			UPDATEBLOCK
			state->inputbits = 0;
			ibuff = 0;
		}
	}

// Pointer to the rest of the data
	pinput = (unsigned int *) data;

// If there something left in the input data, process as many whole blocks as we can
	while (left>=64) {
		UPDATEBLOCK
		left -= 64;
	}

// Copy remaining whole bytes into the buffer
	data = (BitSequence *) pinput;
	while (left) {
		state->inputbuffer[ibuff++] = *data++;
		left--;
	}
	state->inputbits = ibuff*8;

// Handle a part of a byte (only in the last update) - zero least significant bits before storing
	ibits = (int) (databitlen % 8);
	if (ibits) {
		last = *data;
		last >>= 8-ibits;
		last <<= 8-ibits;
		state->inputbuffer[ibuff] = last;
		state->inputbits += ibits;
	}

	return SUCCESS;
}

// -------------------------------------------------------------------------------------------------

// Mangle the copy of Stream 1, optionally mixing with a section of one of the Pools

static void FinalStep ( unsigned int *pworkin, unsigned int *ppool, unsigned int *pworkout ) {
	unsigned int i;

// Swap bits rotationally between elements of the array
	for (i=0; i<STREAM1; i++) {
		pworkout[i] = (pworkin[i] & 0x14141414) | (pworkin[(i+3)%STREAM1] & 0x28282828) | 
					(pworkin[(i+7)%STREAM1] & 0x42424242) | (pworkin[(i+12)%STREAM1] & 0x81818181);
	}

#if DEBUG>2
	printf("  Bits swapped:\n");
	for(i=0; i<STREAM1; i++) {
		if((i&7)==0) printf("  ");
		printf(" %8.8x", pworkout[i]);
		if((i&7)==7) printf("\n");
	}
#endif

// Apply the X-box to each member of the copy array
	for(i=0; i<STREAM1; i++) Xbox(pworkout+i);

#if DEBUG>2
	printf("  X-box applied:\n");
	for(i=0; i<STREAM1; i++) {
		if((i&7)==0) printf("  ");
		printf(" %8.8x", pworkout[i]);
		if((i&7)==7) printf("\n");
	}
#endif

// If it is passed, exclusive-or a section of a Pool into the copy of Stream 1
	if (ppool!=NULL) for(i=0; i<STREAM1; i++) pworkout[i] ^= ppool[i];

#if DEBUG>2
	printf("  Masked:\n");
	for(i=0; i<STREAM1; i++) {
		if((i&7)==0) printf("  ");
		printf(" %8.8x", pworkout[i]);
		if((i&7)==7) printf("\n");
	}
#endif

}

// -------------------------------------------------------------------------------------------------

// API routine to complete the algorithm and return the message digest

HashReturn Final ( hashState *state, BitSequence *hashval ) {
	unsigned int input, *pinput, *ppool;
	unsigned int word1, word2, word3;
	unsigned int i, pass;
	unsigned int work1[STREAM1], work2[STREAM1], *pworkin, *pworkout, *pwork;

#if DEBUG>0
	printf("Final\n");
#endif

// May need to process the contents of the input buffer
	if (state->inputbits) {

	// Zero the end of the buffer (we don't want data going in twice)
		i = (state->inputbits+7) / 8;
		for(; i<64; i++) state->inputbuffer[i] = 0;

	// Process the buffer
		pinput = (unsigned int *) state->inputbuffer;
		UPDATEBLOCK
	}

// Always process the bit count and slab count
	UpdateStep(state, state->inputbits);
	UpdateStep(state, state->inputslabs);

// Perform multiple steps with fixed input to mix the last entries in
	for(i=0; i<FINALUPDATES; i++) UpdateStep(state, i);

#if DEBUG>1
	printf(" Stream 1 after input:\n");
	for(i=0; i<STREAM1; i++) {
		if((i&7)==0) printf(" ");
		printf(" %8.8x", state->stream1[i]);
		if((i&7)==7) printf("\n");
	}
	printf(" Stream 2 after input:\n ");
	for(i=0; i<STREAM2; i++) printf(" %8.8x", state->stream2[i]);
	printf("\n");
	printf(" Stream 3 after input:\n ");
	for(i=0; i<STREAM3; i++) printf(" %8.8x", state->stream3[i]);
	printf("\n");
	printf(" Pool 2 after input:\n");
	for(i=0; i<POOL; i++) {
		if((i&7)==0) printf(" ");
		printf(" %8.8x", state->pool2[i]);
		if((i&7)==7) printf("\n");
	}
	printf(" Pool 3 after input:\n");
	for(i=0; i<POOL; i++) {
		if((i&7)==0) printf(" ");
		printf(" %8.8x", state->pool3[i]);
		if((i&7)==7) printf("\n");
	}
#endif

// Take a copy of Stream 1
	for(i=0; i<STREAM1; i++) work1[i] = state->stream1[i];

// Work arrays are used alternately as input and output
	pworkin = work1;
	pworkout = work2;

// Perform mangling steps, combining with Pool 2
	ppool = state->pool2;
	for(pass=0; pass<POOLFACTOR; pass++) {
#if DEBUG>1
		printf(" Combining with Pool 2 pass %d\n", pass);
#endif
		FinalStep(pworkin, ppool, pworkout);
		pwork = pworkin;
		pworkin = pworkout;
		pworkout = pwork;
		ppool += STREAM1;
	}

// Same with Pool 3
	ppool = state->pool3;
	for(pass=0; pass<POOLFACTOR; pass++) {
#if DEBUG>1
		printf(" Combining with Pool 3 pass %d\n", pass);
#endif
		FinalStep(pworkin, ppool, pworkout);
		pwork = pworkin;
		pworkin = pworkout;
		pworkout = pwork;
		ppool += STREAM1;
	}

// Same steps but with no Pool data
	for(pass=0; pass<FINALSTEPS; pass++) {
#if DEBUG>1
		printf(" Mixing without Pools pass %d\n", pass);
#endif
		FinalStep(pworkin, NULL, pworkout);
		pwork = pworkin;
		pworkin = pworkout;
		pworkout = pwork;
	}

#if DEBUG>1
	printf(" Mangled copy of Stream 1:\n");
	for(i=0; i<STREAM1; i++) {
		if((i&7)==0) printf(" ");
		printf(" %8.8x", pworkin[i]);
		if((i&7)==7) printf("\n");
	}
#endif

// Exclusive-or the mangled copy of Stream 1 with Stream 1
	for(i=0; i<STREAM1; i++) pworkin[i] ^= state->stream1[i];

// And with each section of each Pool to make Final one-way with respect to each of these
	ppool = state->pool2;
	for(pass=0; pass<POOLFACTOR; pass++) {
		for(i=0; i<STREAM1; i++) pworkin[i] ^= ppool[i];
		ppool += STREAM1;
	}

	ppool = state->pool3;
	for(pass=0; pass<POOLFACTOR; pass++) {
		for(i=0; i<STREAM1; i++) pworkin[i] ^= ppool[i];
		ppool += STREAM1;
	}
	
#if DEBUG>1
	printf(" Final digest:\n");
	for(i=0; i<STREAM1; i++) {
		if((i&7)==0) printf(" ");
		printf(" %8.8x", pworkin[i]);
		if((i&7)==7) printf("\n");
	}
#endif

// Return all the bytes required (little-endian transfer)
	for(i=0; i<(state->hashbitlen/32); i++) {
		hashval[4*i] = (unsigned char) pworkin[i];
		hashval[4*i+1] = (unsigned char) (pworkin[i]>>8);
		hashval[4*i+2] = (unsigned char) (pworkin[i]>>16);
		hashval[4*i+3] = (unsigned char) (pworkin[i]>>24);
	}
	return SUCCESS;
}

// -------------------------------------------------------------------------------------------------

// Perform the complete hash process in one call

HashReturn Hash(int hashbitlen, const BitSequence *data, DataLength databitlen,
				BitSequence *hashval) {
	HashReturn ret;
	hashState state;

	ret = Init(&state, hashbitlen);
	if (ret != SUCCESS) return ret;

	ret = Update(&state, data, databitlen);
	if (ret != SUCCESS) return ret;

	ret = Final(&state, hashval);
	return ret;
}

////////////////////////////////////////////////////////////////////////////////////
typedef enum { KAT_SUCCESS = 0, KAT_FILE_OPEN_ERROR = 1, KAT_HEADER_ERROR = 2, KAT_DATA_ERROR = 3, KAT_HASH_ERROR = 4 } STATUS_CODES;

STATUS_CODES    genShortMsg(int hashbitlen);
STATUS_CODES    genLongMsg(int hashbitlen);
STATUS_CODES    genExtremelyLongMsg(int hashbitlen);
STATUS_CODES    genMonteCarlo(int hashbitlen);
int             FindMarker(FILE *infile, const char *marker);
int             ReadHex(FILE *infile, BitSequence *A, int Length, char *str);
void    fprintBstr(FILE *fp, char *S, BitSequence *A, int L);

STATUS_CODES
main()
{
        int             i, ret_val,  bitlens[4] = { 224, 256, 384, 512 };
        unsigned long timeStart = clock();
        for ( i=0; i<4; i++ ) {
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genShortMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genLongMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genExtremelyLongMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genMonteCarlo(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
        }
        printf("overall test time: %lums", clock() - timeStart);
        return KAT_SUCCESS;
}

STATUS_CODES
genShortMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        int                     msglen, msgbytelen, done;
        BitSequence     Msg[256], MD[64];
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genShortMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("ShortMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <ShortMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genShortMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genShortMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        done = 0;
        timeTest(0, logFile);   //test start
        do {
                if ( FindMarker(fp_in, "Len = ") )
                        fscanf(fp_in, "%d", &msglen);
                else {
                        done = 1;
                        break;
                }
                msgbytelen = (msglen+7)/8;

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) {
                        printf("ERROR: unable to read 'Msg' from <ShortMsgKAT.txt>\n");
                        return KAT_DATA_ERROR;
                }
                timeTest(1, logFile);   //unit test start
                Hash(hashbitlen, Msg, msglen, MD);
                timeTest(2, logFile);   //unit test stop
        } while ( !done );
        timeTest(3, logFile);   //test stop
        fclose(logFile);
        printf("\tfinished ShortMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genLongMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        int                     msglen, msgbytelen, done;
        BitSequence     Msg[4288], MD[64];
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genLongMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("LongMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <LongMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genLongMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genLongMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        done = 0;
        timeTest(0, logFile);   //test start
        do {
                if ( FindMarker(fp_in, "Len = ") )
                        fscanf(fp_in, "%d", &msglen);
                else
                        break;
                msgbytelen = (msglen+7)/8;

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) {
                        printf("ERROR: unable to read 'Msg' from <LongMsgKAT.txt>\n");
                        return KAT_DATA_ERROR;
                }
                timeTest(1, logFile);   //unit test start
                Hash(hashbitlen, Msg, msglen, MD);
                timeTest(2, logFile);   //unit test stop
        } while ( !done );
        timeTest(3, logFile);   //test stop
        fclose(logFile);
        printf("\tfinished LongMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genExtremelyLongMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        BitSequence     Text[65], MD[64];
        int                     i, repeat;
        FILE            *fp_in;
        hashState       state;
        HashReturn      retval;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genExtremelyLongMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("ExtremelyLongMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <ExtremelyLongMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genExtremelyLongMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genExtremelyLongMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }


        if ( FindMarker(fp_in, "Repeat = ") )
                fscanf(fp_in, "%d", &repeat);
        else {
                printf("ERROR: unable to read 'Repeat' from <ExtremelyLongMsgKAT.txt>\n");
                return KAT_DATA_ERROR;
        }

        if ( FindMarker(fp_in, "Text = ") )
                fscanf(fp_in, "%s", Text);
        else {
                printf("ERROR: unable to read 'Text' from <ExtremelyLongMsgKAT.txt>\n");
                return KAT_DATA_ERROR;
        }

//      memcpy(Text, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno", 64);
        timeTest(0, logFile);   //test start
        timeTest(1, logFile);   //unit test start
        if ( (retval = Init(&state, hashbitlen)) != KAT_SUCCESS ) {
                printf("Init returned <%d> in genExtremelyLongMsg\n", retval);
                return KAT_HASH_ERROR;
        }
        timeTest(2, logFile);   //unit test stop
        for ( i=0; i<repeat; i++ )
        {
                if (i % 8192 == 0) timeTest(1, logFile);        //unit test start
                if ( (retval = Update(&state, Text, 512)) != KAT_SUCCESS ) {
                        printf("Update returned <%d> in genExtremelyLongMsg\n", retval);
                        return KAT_HASH_ERROR;
                }
                if (i % 8192 == 0) timeTest(2, logFile);        //unit test stop
        }
        timeTest(1, logFile);   //unit test start
        if ( (retval = Final(&state, MD)) != KAT_SUCCESS ) {
                printf("Final returned <%d> in genExtremelyLongMsg\n", retval);
                return KAT_HASH_ERROR;
        }
        timeTest(2, logFile);   //unit test stop
        timeTest(3, logFile);   //test stop
        printf("\tfinished ExtremelyLongMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genMonteCarlo(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        BitSequence     Seed[128], Msg[128], MD[64], Temp[128];
        int                     i, j, bytelen;
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genMonteCarlo_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("MonteCarlo.txt", "r")) == NULL ) {
                printf("Couldn't open <MonteCarlo.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genMonteCarlo: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genMonteCarlo: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        if ( !ReadHex(fp_in, Seed, 128, "Seed = ") ) {
                printf("ERROR: unable to read 'Seed' from <MonteCarlo.txt>\n");
                return KAT_DATA_ERROR;
        }

        bytelen = hashbitlen / 8;
        memcpy(Msg, Seed, 128);
        timeTest(0, logFile);   //test start
        for ( j=0; j<100; j++ ) {
                for ( i=0; i<1000; i++ ) {
                        timeTest(1, logFile);   //unit test start
                        Hash(hashbitlen, Msg, 1024, MD);
                        timeTest(2, logFile);   //unit test stop
                        memcpy(Temp, Msg, 128-bytelen);
                        memcpy(Msg, MD, bytelen);
                        memcpy(Msg+bytelen, Temp, 128-bytelen);
                }
        }
        timeTest(3, logFile);   //test stop
        printf("\tfinished MonteCarloKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
FindMarker(FILE *infile, const char *marker)
{
        char    line[MAX_MARKER_LEN];
        int             i, len;

        len = (int)strlen(marker);
        if ( len > MAX_MARKER_LEN-1 )
                len = MAX_MARKER_LEN-1;

        for ( i=0; i<len; i++ )
                if ( (line[i] = fgetc(infile)) == EOF )
                        return 0;
        line[len] = '\0';

        while ( 1 ) {
                if ( !strncmp(line, marker, len) )
                        return 1;

                for ( i=0; i<len-1; i++ )
                        line[i] = line[i+1];
                if ( (line[len-1] = fgetc(infile)) == EOF )
                        return 0;
                line[len] = '\0';
        }

        // shouldn't get here
        return 0;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
ReadHex(FILE *infile, BitSequence *A, int Length, char *str)
{
        int                     i, ch, started;
        BitSequence     ich;

        if ( Length == 0 ) {
                A[0] = 0x00;
                return 1;
        }
        memset(A, 0x00, Length);
        started = 0;
        if ( FindMarker(infile, str) )
                while ( (ch = fgetc(infile)) != EOF ) {
                        if ( !isxdigit(ch) ) {
                                if ( !started ) {
                                        if ( ch == '\n' )
                                                break;
                                        else
                                                continue;
                                }
                                else
                                        break;
                        }
                        started = 1;
                        if ( (ch >= '0') && (ch <= '9') )
                                ich = ch - '0';
                        else if ( (ch >= 'A') && (ch <= 'F') )
                                ich = ch - 'A' + 10;
                        else if ( (ch >= 'a') && (ch <= 'f') )
                                ich = ch - 'a' + 10;

                        for ( i=0; i<Length-1; i++ )
                                A[i] = (A[i] << 4) | (A[i+1] >> 4);
                        A[Length-1] = (A[Length-1] << 4) | ich;
                }
        else
                return 0;

        return 1;
}

void
fprintBstr(FILE *fp, char *S, BitSequence *A, int L)
{
        int             i;

        fprintf(fp, "%s", S);

        for ( i=0; i<L; i++ )
                fprintf(fp, "%02X", A[i]);

        if ( L == 0 )
                fprintf(fp, "00");

        fprintf(fp, "\n");
}
    
